/*
 * This file is part of the CmBacktrace Library.
 *
 * Copyright (c) 2016-2020, Armink, <armink.ztl@gmail.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * 'Software'), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * Function: It is the macro definition head file for this library.
 * Created on: 2016-12-15
 */

#ifndef _CMB_DEF_H_
#define _CMB_DEF_H_

#include <cmb_cfg.h>
#include <stdint.h>
#include <stdlib.h>

/* library software version number */
#define CMB_SW_VERSION "1.4.0"

#define CMB_CPU_ARM_CORTEX_M0 0
#define CMB_CPU_ARM_CORTEX_M3 1
#define CMB_CPU_ARM_CORTEX_M4 2
#define CMB_CPU_ARM_CORTEX_M7 3

#define CMB_OS_PLATFORM_RTT      0
#define CMB_OS_PLATFORM_UCOSII   1
#define CMB_OS_PLATFORM_UCOSIII  2
#define CMB_OS_PLATFORM_FREERTOS 3

#define CMB_PRINT_LANGUAGE_ENGLISH 0
#define CMB_PRINT_LANGUAGE_CHINESE 1

/* name max length, default size: 32 */
#ifndef CMB_NAME_MAX
    #define CMB_NAME_MAX 32
#endif

/* print information language, default is English */
#ifndef CMB_PRINT_LANGUAGE
    #define CMB_PRINT_LANGUAGE CMB_PRINT_LANGUAGE_ENGLISH
#endif

#if defined(__CC_ARM)
    /* C stack block name, default is STACK */
    #ifndef CMB_CSTACK_BLOCK_NAME
        #define CMB_CSTACK_BLOCK_NAME STACK
    #endif
    /* code section name, default is ER_IROM1 */
    #ifndef CMB_CODE_SECTION_NAME
        #define CMB_CODE_SECTION_NAME ER_IROM1
    #endif
#elif defined(__ICCARM__)
    /* C stack block name, default is 'CSTACK' */
    #ifndef CMB_CSTACK_BLOCK_NAME
        #define CMB_CSTACK_BLOCK_NAME "CSTACK"
    #endif
    /* code section name, default is '.text' */
    #ifndef CMB_CODE_SECTION_NAME
        #define CMB_CODE_SECTION_NAME ".text"
    #endif
#elif defined(__GNUC__)
    /* C stack block start address, defined on linker script file, default is _sstack */
    #ifndef CMB_CSTACK_BLOCK_START
        #define CMB_CSTACK_BLOCK_START _sstack
    #endif
    /* C stack block end address, defined on linker script file, default is _estack */
    #ifndef CMB_CSTACK_BLOCK_END
        #define CMB_CSTACK_BLOCK_END _estack
    #endif
    /* code section start address, defined on linker script file, default is _stext */
    #ifndef CMB_CODE_SECTION_START
        #define CMB_CODE_SECTION_START _stext
    #endif
    /* code section end address, defined on linker script file, default is _etext */
    #ifndef CMB_CODE_SECTION_END
        #define CMB_CODE_SECTION_END _etext
    #endif
#else
    #error "not supported compiler"
#endif

/* supported function call stack max depth, default is 16 */
#ifndef CMB_CALL_STACK_MAX_DEPTH
    #define CMB_CALL_STACK_MAX_DEPTH 16
#endif

/* system handler control and state register */
#ifndef CMB_SYSHND_CTRL
    #define CMB_SYSHND_CTRL (*(volatile unsigned int*)(0xE000ED24u))
#endif

/* memory management fault status register */
#ifndef CMB_NVIC_MFSR
    #define CMB_NVIC_MFSR (*(volatile unsigned char*)(0xE000ED28u))
#endif

/* bus fault status register */
#ifndef CMB_NVIC_BFSR
    #define CMB_NVIC_BFSR (*(volatile unsigned char*)(0xE000ED29u))
#endif

/* usage fault status register */
#ifndef CMB_NVIC_UFSR
    #define CMB_NVIC_UFSR (*(volatile unsigned short*)(0xE000ED2Au))
#endif

/* hard fault status register */
#ifndef CMB_NVIC_HFSR
    #define CMB_NVIC_HFSR (*(volatile unsigned int*)(0xE000ED2Cu))
#endif

/* debug fault status register */
#ifndef CMB_NVIC_DFSR
    #define CMB_NVIC_DFSR (*(volatile unsigned short*)(0xE000ED30u))
#endif

/* memory management fault address register */
#ifndef CMB_NVIC_MMAR
    #define CMB_NVIC_MMAR (*(volatile unsigned int*)(0xE000ED34u))
#endif

/* bus fault manage address register */
#ifndef CMB_NVIC_BFAR
    #define CMB_NVIC_BFAR (*(volatile unsigned int*)(0xE000ED38u))
#endif

/* auxiliary fault status register */
#ifndef CMB_NVIC_AFSR
    #define CMB_NVIC_AFSR (*(volatile unsigned short*)(0xE000ED3Cu))
#endif

/**
 * Cortex-M fault registers
 */
struct cmb_hard_fault_regs {
    struct {
        unsigned int r0;  // Register R0
        unsigned int r1;  // Register R1
        unsigned int r2;  // Register R2
        unsigned int r3;  // Register R3
        unsigned int r12; // Register R12
        unsigned int lr;  // Link register
        unsigned int pc;  // Program counter
        union {
            unsigned int value;
            struct {
                unsigned int IPSR : 8;  // Interrupt Program Status register (IPSR)
                unsigned int EPSR : 19; // Execution Program Status register (EPSR)
                unsigned int APSR : 5;  // Application Program Status register (APSR)
            } bits;
        } psr; // Program status register.
    } saved;

    union {
        unsigned int value;
        struct {
            unsigned int MEMFAULTACT : 1; // Read as 1 if memory management fault is active
            unsigned int BUSFAULTACT : 1; // Read as 1 if bus fault exception is active
            unsigned int UnusedBits1 : 1;
            unsigned int USGFAULTACT : 1; // Read as 1 if usage fault exception is active
            unsigned int UnusedBits2 : 3;
            unsigned int SVCALLACT : 1;  // Read as 1 if SVC exception is active
            unsigned int MONITORACT : 1; // Read as 1 if debug monitor exception is active
            unsigned int UnusedBits3 : 1;
            unsigned int PENDSVACT : 1;      // Read as 1 if PendSV exception is active
            unsigned int SYSTICKACT : 1;     // Read as 1 if SYSTICK exception is active
            unsigned int USGFAULTPENDED : 1; // Usage fault pended; usage fault started but was replaced by a higher-priority exception
            unsigned int MEMFAULTPENDED : 1; // Memory management fault pended; memory management fault started but was replaced by a higher-priority exception
            unsigned int BUSFAULTPENDED : 1; // Bus fault pended; bus fault handler was started but was replaced by a higher-priority exception
            unsigned int SVCALLPENDED : 1;   // SVC pended; SVC was started but was replaced by a higher-priority exception
            unsigned int MEMFAULTENA : 1;    // Memory management fault handler enable
            unsigned int BUSFAULTENA : 1;    // Bus fault handler enable
            unsigned int USGFAULTENA : 1;    // Usage fault handler enable
        } bits;
    } syshndctrl; // System Handler Control and State Register (0xE000ED24)

    union {
        unsigned char value;
        struct {
            unsigned char IACCVIOL : 1; // Instruction access violation
            unsigned char DACCVIOL : 1; // Data access violation
            unsigned char UnusedBits : 1;
            unsigned char MUNSTKERR : 1; // Unstacking error
            unsigned char MSTKERR : 1;   // Stacking error
            unsigned char MLSPERR : 1;   // Floating-point lazy state preservation (M4/M7)
            unsigned char UnusedBits2 : 1;
            unsigned char MMARVALID : 1; // Indicates the MMAR is valid
        } bits;
    } mfsr;            // Memory Management Fault Status Register (0xE000ED28)
    unsigned int mmar; // Memory Management Fault Address Register (0xE000ED34)

    union {
        unsigned char value;
        struct {
            unsigned char IBUSERR : 1;    // Instruction access violation
            unsigned char PRECISERR : 1;  // Precise data access violation
            unsigned char IMPREISERR : 1; // Imprecise data access violation
            unsigned char UNSTKERR : 1;   // Unstacking error
            unsigned char STKERR : 1;     // Stacking error
            unsigned char LSPERR : 1;     // Floating-point lazy state preservation (M4/M7)
            unsigned char UnusedBits : 1;
            unsigned char BFARVALID : 1; // Indicates BFAR is valid
        } bits;
    } bfsr;            // Bus Fault Status Register (0xE000ED29)
    unsigned int bfar; // Bus Fault Manage Address Register (0xE000ED38)

    union {
        unsigned short value;
        struct {
            unsigned short UNDEFINSTR : 1; // Attempts to execute an undefined instruction
            unsigned short INVSTATE : 1;   // Attempts to switch to an invalid state (e.g., ARM)
            unsigned short INVPC : 1;      // Attempts to do an exception with a bad value in the EXC_RETURN number
            unsigned short NOCP : 1;       // Attempts to execute a coprocessor instruction
            unsigned short UnusedBits : 4;
            unsigned short UNALIGNED : 1;  // Indicates that an unaligned access fault has taken place
            unsigned short DIVBYZERO0 : 1; // Indicates a divide by zero has taken place (can be set only if DIV_0_TRP is set)
        } bits;
    } ufsr; // Usage Fault Status Register (0xE000ED2A)

    union {
        unsigned int value;
        struct {
            unsigned int UnusedBits : 1;
            unsigned int VECTBL : 1; // Indicates hard fault is caused by failed vector fetch
            unsigned int UnusedBits2 : 28;
            unsigned int FORCED : 1;   // Indicates hard fault is taken because of bus fault/memory management fault/usage fault
            unsigned int DEBUGEVT : 1; // Indicates hard fault is triggered by debug event
        } bits;
    } hfsr; // Hard Fault Status Register (0xE000ED2C)

    union {
        unsigned int value;
        struct {
            unsigned int HALTED : 1;   // Halt requested in NVIC
            unsigned int BKPT : 1;     // BKPT instruction executed
            unsigned int DWTTRAP : 1;  // DWT match occurred
            unsigned int VCATCH : 1;   // Vector fetch occurred
            unsigned int EXTERNAL : 1; // EDBGRQ signal asserted
        } bits;
    } dfsr; // Debug Fault Status Register (0xE000ED30)

    unsigned int afsr; // Auxiliary Fault Status Register (0xE000ED3C), Vendor controlled (optional)
};

/* assert for developer. */
#define CMB_ASSERT(EXPR)                                                   \
    if (!(EXPR)) {                                                         \
        cmb_println("(%s) has assert failed at %s.", #EXPR, __FUNCTION__); \
        while (1)                                                          \
            ;                                                              \
    }

/* ELF(Executable and Linking Format) file extension name for each compiler */
#if defined(__CC_ARM)
    #define CMB_ELF_FILE_EXTENSION_NAME ".axf"
#elif defined(__ICCARM__)
    #define CMB_ELF_FILE_EXTENSION_NAME ".out"
#elif defined(__GNUC__)
    #define CMB_ELF_FILE_EXTENSION_NAME ".elf"
#else
    #error "not supported compiler"
#endif

#ifndef cmb_println
    #error "cmb_println isn't defined in 'cmb_cfg.h'"
#endif

#ifndef CMB_CPU_PLATFORM_TYPE
    #error "CMB_CPU_PLATFORM_TYPE isn't defined in 'cmb_cfg.h'"
#endif

#if (defined(CMB_USING_BARE_METAL_PLATFORM) && defined(CMB_USING_OS_PLATFORM))
    #error "CMB_USING_BARE_METAL_PLATFORM and CMB_USING_OS_PLATFORM only one of them can be used"
#elif defined(CMB_USING_OS_PLATFORM)
    #if !defined(CMB_OS_PLATFORM_TYPE)
        #error "CMB_OS_PLATFORM_TYPE isn't defined in 'cmb_cfg.h'"
    #endif /* !defined(CMB_OS_PLATFORM_TYPE) */
    #if (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_RTT)
        #include <rtthread.h>
    #elif (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_UCOSII)
        #include <ucos_ii.h>
    #elif (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_UCOSIII)
        #include <os.h>
    #elif (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_FREERTOS)
        #include <FreeRTOS.h>
extern uint32_t* vTaskStackAddr(void); /* need to modify the FreeRTOS/tasks source code */
extern uint32_t vTaskStackSize(void);
extern char* vTaskName(void);
    #else
        #error "not supported OS type"
    #endif /* (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_RTT) */
#endif     /* (defined(CMB_USING_BARE_METAL_PLATFORM) && defined(CMB_USING_OS_PLATFORM)) */

/* include or export for supported cmb_get_msp, cmb_get_psp and cmb_get_sp function */
#if defined(__CC_ARM)
static __inline __asm uint32_t cmb_get_msp(void)
{
    mrs r0, msp bx lr
}
static __inline __asm uint32_t cmb_get_psp(void)
{
    mrs r0, psp bx lr
}
static __inline __asm uint32_t cmb_get_sp(void)
{
    mov r0, sp bx lr
}
#elif defined(__ICCARM__)
    /* IAR iccarm specific functions */
    /* Close Raw Asm Code Warning */
    #pragma diag_suppress = Pe940
static uint32_t cmb_get_msp(void)
{
    __asm("mrs r0, msp");
    __asm("bx lr");
}
static uint32_t cmb_get_psp(void)
{
    __asm("mrs r0, psp");
    __asm("bx lr");
}
static uint32_t cmb_get_sp(void)
{
    __asm("mov r0, sp");
    __asm("bx lr");
}
    #pragma diag_default = Pe940
#elif defined(__GNUC__)
__attribute__((always_inline)) static inline uint32_t cmb_get_msp(void)
{
    register uint32_t result;
    __asm volatile("MRS %0, msp\n"
                   : "=r"(result));
    return (result);
}
__attribute__((always_inline)) static inline uint32_t cmb_get_psp(void)
{
    register uint32_t result;
    __asm volatile("MRS %0, psp\n"
                   : "=r"(result));
    return (result);
}
__attribute__((always_inline)) static inline uint32_t cmb_get_sp(void)
{
    register uint32_t result;
    __asm volatile("MOV %0, sp\n"
                   : "=r"(result));
    return (result);
}
#else
    #error "not supported compiler"
#endif

#endif /* _CMB_DEF_H_ */
